package org.example.platerecognize.utils;

import org.apache.commons.lang3.SystemUtils;
import org.example.platerecognize.dao.BorderMat;
import org.example.platerecognize.dao.ImageMat;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.highgui.HighGui;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.NoSuchElementException;


/**
 * @author Zhou
 * @date 10:17 2024/7/16
 */
public final class CommonConst {
    public static final String RESOURCES_PATH;

    static {
        if (SystemUtils.IS_OS_WINDOWS) {
            var path = (ClassLoader.getSystemResource("").getPath()).replace("\\", "/");
            RESOURCES_PATH = path.startsWith("/") ? path.substring(1) : path;
        } else {
            RESOURCES_PATH = (ClassLoader.getSystemResource("").getPath()).replace("\\", "/");
        }
    }

    public static void release(Mat... mats) {
        for (Mat mat : mats) {
            if (null != mat) {
                try {
                    mat.release();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    mat = null;
                }
            }
        }
    }

    public static void release(ImageMat... imageMats) {
        for (ImageMat imageMat : imageMats) {
            if (null != imageMat) {
                try {
                    imageMat.mat().release();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    imageMat = null;
                }
            }
        }
    }

    public static void release(BorderMat... borderMats) {
        for (BorderMat borderMat : borderMats) {
            if (null != borderMat) {
                try {
                    borderMat.release();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    borderMat = null;
                }
            }
        }
    }

    public static void printInfo(Mat image, String... titles) {
        var title = titles.length > 0 ? titles[0] : "image";
        System.out.println(String.format("[%s] >>> cols: %d, rows: %d, channels: %d, type: %d, \n \t width: %d, height: %d, arrayDepth: %d", title, image.cols(), image.rows(), image.channels(), image.type(), image.width(), image.height(), image.depth()));
    }

    /**
     * 显示图片，用于数据调试
     */
    public static void show(Mat mat, String... titles) {
        var title = titles.length > 0 ? titles[0] : "image";

        HighGui.namedWindow(title, HighGui.WINDOW_NORMAL);
        //# 获取图像的宽度和高度
        System.out.println(String.format("%s %s", title, mat.size()));
        //# 设置窗口大小为图像的大小
        HighGui.resizeWindow(title, 960, 540);

        HighGui.imshow(title, mat);
        HighGui.waitKey();
    }

    /**
     * 显示图片，用于数据调试
     */
    public static void show(ImageMat mat, String... titles) {
        show(mat.mat(), titles);
    }

    /**
     * Mat保存为图片
     *
     * @param name
     * @param mat
     */
    public static void saveToPicture(String name, Mat mat) {
        final String extension = name.contains(".") ? name.substring(name.lastIndexOf(".") + 1) : "jpg";

        BufferedImage image;
        //保存图像：方式一
        {
            int type = mat.channels() == 1 ? BufferedImage.TYPE_BYTE_GRAY : BufferedImage.TYPE_3BYTE_BGR;
            int bufferSize = mat.cols() * mat.rows() * mat.channels();
            byte[] buffer = new byte[bufferSize];

            mat.get(0, 0, buffer); // get all the pixels

            if (type == BufferedImage.TYPE_3BYTE_BGR) {
                //将bgr 的 b 和 r 交换
                for (int i = 0; i < bufferSize; i += 3) {
                    byte blue = buffer[i];
                    buffer[i] = buffer[i + 2];
                    buffer[i + 2] = blue;
                }
            }
            image = new BufferedImage(mat.cols(), mat.rows(), type);
            image.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), buffer);
        }

        //保存图像：方式二
//        Java2DFrameConverter java2DFrameConverterOutput = new Java2DFrameConverter();
//        BufferedImage outputImg = java2DFrameConverterOutput.convert(new OpenCVFrameConverter.ToMat().convert(image));
        String output_path = "C:\\Users\\hang.zhou\\Pictures\\";
        try {
            ImageIO.write(image, extension, new File(output_path + name));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 对点进行中心旋转
     *
     * @param center 中心点
     * @param angle  旋转角度
     * @return 旋转后的角
     */
    public static Point rotation(Point center, Point point, float angle) {
        double k = Math.toRadians(angle);
        float nx1 = (float) ((point.x - center.x) * Math.cos(k) + (point.y - center.y) * Math.sin(k) + center.x);
        float ny1 = (float) (-(point.x - center.x) * Math.sin(k) + (point.y - center.y) * Math.cos(k) + center.y);
        return new Point(nx1, ny1);
    }

    public static float distance(Point a, Point b) {
        double px = a.x - b.x;
        double py = a.y - b.y;
        return (float) Math.sqrt(px * px + py * py);
    }

    public static double[] floatToDouble(float[] arr) {
        if (arr.length == 0)
            return new double[0];

        double[] arr2 = new double[arr.length];
        for (int i = 0; i < arr.length; i++) {
            arr2[i] = arr[i];
        }
        return arr2;
    }

    /**
     * softMax
     *
     * @param tensor
     * @return
     */
    public static double[] softMax(double[] tensor) {
        double maxValue = Arrays.stream(tensor).max()
                .orElseThrow(() -> new NoSuchElementException("No value present"));

        double[] expArr = Arrays.stream(tensor).map(y -> Math.exp(y - maxValue)).toArray();
        double totalExp = Arrays.stream(expArr).sum();

        return Arrays.stream(expArr).map(e -> e / totalExp).toArray();
    }

    /**
     * softMax
     *
     * @param tensor
     * @return
     */
    public double[][] softMax(double[][] tensor) {
        double[][] result = new double[tensor.length][];
        for (int i = 0; i < result.length; i++) {
            result[i] = softMax(tensor[i]);
        }
        return result;
    }
}
